#include "stdafx.h"
#include "Utility.h"
#include "LogClass.h"

LogClass::LogClass(LPCWSTR logfname, bool clearFile, bool openFileNow)
{
	m_lastCheckTick.Clear();
	if (logfname) wcscpy_s(fName, logfname); else fName[0] = 0;
	maxBufLength = 10240;
	pBuffer = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, maxBufLength*sizeof(wchar_t));
	if (clearFile && fName[0]) DeleteFile(fName);
	fLog = nullptr;
	if (openFileNow) OpenFile(false);
}

LogClass::~LogClass()
{
	flush(true); CloseFile(false);
	HeapFree(GetProcessHeap(), 0, pBuffer);
}

void LogClass::setFileName(LPCWSTR logfname, bool openFileNow)
{
	lock_flush.Lock();
	if (fLog) CloseFile(true);
	wcscpy_s(fName, logfname);
	if (openFileNow) OpenFile(true);
	lock_flush.Unlock();
}

void LogClass::OpenFile(bool locked)
{
	if (!locked) lock_flush.Lock();
	if (fLog) CloseFile(true);
	if (fName[0]) fLog = _wfsopen(fName, L"a+tc", _SH_DENYWR);
	if (!locked) lock_flush.Unlock();
}

void LogClass::CloseFile(bool locked)
{
	if (!locked) lock_flush.Lock();
	if (fLog) { fclose(fLog); fLog = nullptr; }
	if (!locked) lock_flush.Unlock();
}

void LogClass::flush(bool waitIfLocked, intptr_t wrNum)
{
	if (waitIfLocked)
		lock_flush.Lock();
	else if (m_logList.IsEmpty() || !lock_flush.TestLock())
		return;
	bool fOpened = fLog != nullptr; if (!fOpened) OpenFile(true);
	for (intptr_t i = 0; wrNum <= 0 || i < wrNum; i++)
	{
		CLogNode *pDel = m_logList.GetNode(); if (!pDel) break;
		if (fLog) fwprintf_s(fLog, L"%s\n", pDel->getLogText());
		delete pDel;
	}
	if (!fOpened)
		CloseFile(true);
	else if (fLog)
		fflush(fLog);
	lock_flush.Unlock();
}

LPVOID LogClass::GetContent(intptr_t &sz)
{
	LPVOID result = nullptr;
	if (fName[0])
	{
		flush(); lock_flush.Lock();
		FILE *f = _wfsopen(fName, L"rb", _SH_DENYNO);
		do {
			if (!f) break;
			fseek(f, 0, SEEK_END); sz = ftell(f); if (sz <= 0) break;
			result = HeapAlloc(GetProcessHeap(), 0, sz); if (!result) break;
			fseek(f, 0, SEEK_SET); fread(result, sz, 1, f);
		} while (0);
		if (f) fclose(f);
		lock_flush.Unlock();
	}
	return result;
}

void LogClass::addString(LPCWSTR sString)
{
	if (!sString) return;
	m_logList.AddNode(new CLogNode(copyStr(sString)));
}

void LogClass::_add(LPCWSTR sFormat, va_list pArgList)
{
	lock_buffer.Lock();
	_vsntprintf_s(pBuffer, maxBufLength, maxBufLength, sFormat, pArgList);
	addString(pBuffer);
	lock_buffer.Unlock();
}

void LogClass::add(LPCWSTR sFormat, ...)
{
	va_list pArgList;
	va_start(pArgList, sFormat);
	_add(sFormat, pArgList);
	va_end(pArgList);
}

void LogClass::addFlush(LPCWSTR sFormat, ...)
{
	va_list pArgList;
	va_start(pArgList, sFormat);
	_add(sFormat, pArgList);
	va_end(pArgList);
	flush();
}

void LogClass::checkFlush()
{
	TICK tk = TICK::Now(); if (m_lastCheckTick.IsAvailable() && tk < m_lastCheckTick) return;
	m_lastCheckTick = tk + DFTick::ms(5000);	//60000
	flush();
}

LPWSTR LogClass::getBuffer(size_t &bufLength)
{
	lock_buffer.Lock();
	bufLength = maxBufLength;
	return pBuffer;
}

void LogClass::releaseBuffer()
{
	lock_buffer.Unlock();
}

#ifndef _USE_TIMER_OUTSIDE_

#define PRETABS(n,level)	wchar_t preTabs[n]; for (intptr_t i = 0; i < level; i++) preTabs[i] = L'\t'; preTabs[level] = 0;

#define	addXMLNULL		add(L"%s<%s* %s=null />", preTabs, classname, name)
#define	addXMLStart		add(L"%s<%s* %s=%08x>", preTabs, classname, name, (intptr_t)obj)
#define	addXMLEnd		add(L"%s</%s* %s>", preTabs, classname, name)
#define	addXMLBSTR(name,names)		\
{						\
	_bstr_t name;		\
	add(L"%s\t<%s = %s />", preTabs, names,		\
		FAILED(obj->name(name.GetAddress())) ? L"null" : (LPCWSTR)name);	\
}
#define	addXMLINT(name,names)	\
{									\
	int name;						\
	if (FAILED(obj->name(&name)))	\
		add(L"%s\t<%s = null />", preTabs, names);	\
	else							\
		add(L"%s\t<%s = %d />", preTabs, names, name);	\
}
#define	addXMLLONGLONG(name,names)	\
{									\
	long long name;						\
	if (FAILED(obj->name(&name)))	\
		add(L"%s\t<%s = null />", preTabs, names);	\
		else							\
		add(L"%s\t<%s = %d />", preTabs, names, (intptr_t)name);	\
}
#define	addXMLObject(addFunc,objPtr,name,names)	\
{								\
	objPtr name;				\
	addFunc(level + 1, names,	\
		(FAILED(obj->name(&name.GetInterfacePtr())) || !name) ? nullptr : name.GetInterfacePtr());	\
}
#define	addXMLPropBSTR(prop,names)	\
{								\
	CComVariant v;				\
	if (FAILED(prop->Read(names, &v, nullptr)))			\
		add(L"%s\t<%s = null />", preTabs, names);		\
	else						\
		add(L"%s\t<%s = %s />", preTabs, names, v.bstrVal);	\
}

void LogClass::addXMLData(intptr_t level, LPCWSTR name, const byte * obj, intptr_t objLen)
{
	if (!CheckDirectory(L"Streams")) return;
	PRETABS(100, level);
	wchar_t stm[10]; wsprintf(stm, L"%08x", (intptr_t)obj);
	wchar_t stmFName[20]; stmFName[0] = 0;
	do {
		if (!obj) break;
		wchar_t stmFullName[40]; FILE * f = nullptr;
		for (intptr_t i = 0;; i++)
		{
			wsprintf(stmFullName, L"Streams\\%s_%06d", stm, i);
			if (::GetFileAttributes(stmFullName) != INVALID_FILE_ATTRIBUTES) continue;
			f = _wfsopen(stmFullName, L"wb", _SH_DENYWR);
			if (f) wsprintf(stmFName, L"%s_%06d", stm, i);
			break;
		}
		if (f)
		{
			fwrite(obj, objLen, 1, f);
			fclose(f);
		}
	} while (0);
	add(L"%s<%s=%08x %s />", preTabs, name, (intptr_t)obj, stmFName);
}

void LogClass::addIStream(intptr_t level, LPCWSTR name, IStream* obj, LPCWSTR classname)
{
	if (!CheckDirectory(L"Streams")) return;
	if (!classname) classname = L"IStream";
	PRETABS(100, level);
	wchar_t stm[10]; wsprintf(stm, L"%08x", (intptr_t)obj);
	wchar_t stmFName[20]; stmFName[0] = 0;
	do {
		if (!obj) break;
		IStreamPtr cloneStream;
		if (FAILED(obj->Clone(&cloneStream.GetInterfacePtr())) || !cloneStream) break;
		wchar_t stmFullName[40]; FILE * f = nullptr;
		for (intptr_t i = 0;; i++)
		{
			wsprintf(stmFullName, L"Streams\\%s_%06d", stm, i);
			if (::GetFileAttributes(stmFullName) != INVALID_FILE_ATTRIBUTES) continue;
			f = _wfsopen(stmFullName, L"wb", _SH_DENYWR);
			if (f) wsprintf(stmFName, L"%s_%06d", stm, i);
			break;
		}
		if (f)
		{
			do {
				LARGE_INTEGER zeroOffset; zeroOffset.QuadPart = 0;
				if (FAILED(cloneStream->Seek(zeroOffset, STREAM_SEEK_SET, nullptr))) break;
				for (;;)
				{
					byte buf[1024]; ULONG bytesRead;
					if (cloneStream->Read(buf, 1024, &bytesRead) != S_OK) break;
					fwrite(buf, bytesRead, 1, f);
				}
			} while (0);
			fclose(f);
		}
	} while (0);
	add(L"%s<%s* %s=%08x %s />", preTabs, classname, name, (intptr_t)obj, stmFName);
}

void LogClass::addIWebError(intptr_t level, LPCWSTR name, IWebError* obj, LPCWSTR classname)
{
	if (!classname) classname = L"IWebError";
	PRETABS(100, level);
	if (!obj)
		addXMLNULL;
	else
	{
		addXMLStart;
		addXMLINT(code, L"code");
		addXMLBSTR(domain, L"domain");
		addXMLBSTR(localizedDescription, L"localizedDescription");
		//addXMLBSTR(localizedFailureReason, L"localizedFailureReason");
		//addXMLBSTR(localizedRecoverySuggestion, L"localizedRecoverySuggestion");
		addXMLBSTR(failingURL, L"failingURL");
		addXMLEnd;
	}
}

void LogClass::addIWebURLRequest(intptr_t level, LPCWSTR name, IWebURLRequest* obj, LPCWSTR classname)
{
	if (!classname) classname = L"IWebURLRequest";
	PRETABS(100, level);
	if (!obj)
		addXMLNULL;
	else
	{
		addXMLStart;
		addXMLBSTR(mainDocumentURL, L"mainDocumentURL");
		addXMLBSTR(URL, L"URL");
		addXMLBSTR(HTTPMethod, L"HTTPMethod");
		_bstr_t name;
		add(L"%s\t<%s = %s />", preTabs, L"Content-Length",
			FAILED(obj->valueForHTTPHeaderField(CComBSTR("Content-Length"), name.GetAddress())) ? L"null" : (LPCWSTR)name);
		add(L"%s\t<%s = %s />", preTabs, L"Date",
			FAILED(obj->valueForHTTPHeaderField(CComBSTR("Date"), name.GetAddress())) ? L"null" : (LPCWSTR)name);
		//addXMLObject(addIStream, IStreamPtr, HTTPBodyStream, L"HTTPBodyStream");
		addXMLEnd;
	}
}
void LogClass::addIWebMutableURLRequest(intptr_t level, LPCWSTR name, IWebMutableURLRequest* obj, LPCWSTR classname)
{
	if (!classname) classname = L"IWebMutableURLRequest";
	addIWebURLRequest(level, name, obj, classname);
}

void LogClass::addIWebURLResponse(intptr_t level, LPCWSTR name, IWebURLResponse* obj, LPCWSTR classname)
{
	if (!classname) classname = L"IWebURLResponse";
	PRETABS(100, level);
	if (!obj)
		addXMLNULL;
	else
	{
		addXMLStart;
		addXMLLONGLONG(expectedContentLength, L"expectedContentLength");
		addXMLBSTR(MIMEType, L"MIMEType");
		addXMLBSTR(suggestedFilename, L"suggestedFilename");
		addXMLBSTR(textEncodingName, L"textEncodingName");
		addXMLBSTR(URL, L"URL");
		addXMLEnd;
	}
}

void LogClass::addIWebHTTPURLResponse(intptr_t level, LPCWSTR name, IWebHTTPURLResponse* obj, LPCWSTR classname)
{
	if (!classname) classname = L"IWebHTTPURLResponse";
	PRETABS(100, level);
	if (!obj)
		addXMLNULL;
	else
	{
		addXMLStart;
		addXMLINT(statusCode, L"statusCode");
		IPropertyBagPtr prop;
		if (!FAILED(obj->allHeaderFields(&prop.GetInterfacePtr())) && prop)
		{
			addXMLPropBSTR(prop, L"Date");
		}
		addXMLEnd;
	}
}

void LogClass::addIWebResource(intptr_t level, LPCWSTR name, IWebResource* obj, LPCWSTR classname)
{
	if (!classname) classname = L"IWebResource";
	PRETABS(100, level);
	if (!obj)
		addXMLNULL;
	else
	{
		addXMLStart;
		addXMLObject(addIStream, IStreamPtr, data, L"data");
		addXMLBSTR(URL, L"URL");
		addXMLBSTR(MIMEType, L"MIMEType");
		addXMLBSTR(textEncodingName, L"textEncodingName");
		addXMLBSTR(frameName, L"frameName");
		addXMLEnd;
	}
}

void LogClass::addIWebDataSource(intptr_t level, LPCWSTR name, IWebDataSource* obj, LPCWSTR classname)
{
	if (!classname) classname = L"IWebDataSource";
	PRETABS(100, level);
	if (!obj)
		addXMLNULL;
	else
	{
		addXMLStart;
		addXMLObject(addIStream, IStreamPtr, data, L"data");
		addXMLObject(addIWebURLRequest, IWebURLRequestPtr, initialRequest, L"initialRequest");
		addXMLObject(addIWebMutableURLRequest, IWebMutableURLRequestPtr, request, L"request");
		addXMLObject(addIWebURLResponse, IWebURLResponsePtr, response, L"response");
		addXMLBSTR(textEncodingName, L"textEncodingName");
		addXMLBSTR(pageTitle, L"pageTitle");
		addXMLEnd;
	}
}

void LogClass::addIWebView(intptr_t level, LPCWSTR name, IWebView* obj, LPCWSTR classname)
{
	if (!classname) classname = L"IWebView";
	PRETABS(100, level);
	if (!obj)
		addXMLNULL;
	else
	{
		addXMLStart;
		addXMLEnd;
	}
}

void LogClass::addIWebFrame(intptr_t level, LPCWSTR name, IWebFrame* obj, LPCWSTR classname)
{
	if (!classname) classname = L"IWebFrame";
	PRETABS(100, level);
	if (!obj)
		addXMLNULL;
	else
	{
		addXMLStart;
		addXMLObject(addIWebDataSource, IWebDataSourcePtr, dataSource, L"dataSource");
		addXMLObject(addIWebDataSource, IWebDataSourcePtr, provisionalDataSource, L"provisionalDataSource");
		addXMLEnd;
	}
}
#endif
